跳到主要内容

ES2022 的特性是聚焦于 提升代码的可读性(如类字段声明、at() 方法)、增强封装性(如私有成员)、优化异步流程(如顶层 await)和错误处理(如 Error Cause)。

一、 类字段声明

在之前的 JavaScript 中,字段需要在 constructor 中通过 this.xxx 声明,代码分散且不直观。新的版本将通过在类体中直接声明实例字段,无需依赖 constructor

class Dog {
name: 'Lucky';
age;

constructor(age) {
this.age = age || 0;
}
}

const lucky = new Dog(3);
console.log(lucky.name); // "lucky"
console.log(lucky.age); // 3

二、 私有方法和私有访问器

此前缺乏真正的私有成员,通过命名约定(如 _private)模拟的私有属性仍可被外部访问,存在安全隐患。现在通过 # 前缀定义私有字段、私有方法和私有访问器(gettersetter),仅允许在类的内部访问。

class BankAccount {
#balance = 0; // 私有字段

// 私有方法
#validateAmount(amount) {
return amount > 0;
}

// 私有访问器(getter)
get #currentBalance() {
return this.#balance;
}

deposit(amount) {
if (this.#validateAmount(amount)) {
// 类内部可访问私有方法
this.#balance += amount;
}
}

getBalance() {
return this.#currentBalance; // 类内部可访问私有getter
}
}

const account = new BankAccount();
account.deposit(100);
console.log(account.getBalance()); // 100

// 外部访问私有成员会报错
console.log(account.#balance); // SyntaxError
account.#validateAmount(50); // SyntaxError

三、 静态私有成员

静态成员(类级别的成员)同样需要私有性,但此前无法限制外部访问。通过 # 前缀定义静态私有字段和方法,仅允许在类自身内部访问(无法被实例或子类访问)。

class MathUtil {
static #PI = 3.14159; // 静态私有字段

static #calculateArea(radius) {
// 静态私有方法
return this.#PI * radius ** 2;
}

static getCircleArea(radius) {
return this.#calculateArea(radius); // 类内部可访问静态私有成员
}
}

console.log(MathUtil.getCircleArea(2)); // 12.56636

// 外部访问静态私有成员会报错
console.log(MathUtil.#PI); // SyntaxError
MathUtil.#calculateArea(2); // SyntaxError

四、 类静态初始化块

在类中使用 static {} 定义静态初始块,用于执行类级别的初始化逻辑(如加载配置、初始化静态字段)。静态块在类定义是执行,仅执行一次。

class Config {
static apiURL;

static {
// 静态初始化块,加载配置
Config.apiURL = fetch('api/config').then(res => res.json);
}
}

// Promise 对象(静态块已执行,但异步操作未完成)
console.log(Config.apiURL);

五、 顶层 await

await 之前仅允许在 async 函数中使用,模块化初始化时需要异步操作(如加载配置),需嵌套在 async 函数中,代码繁琐。新的版本允许在模块的顶层直接使用 await,暂停模块执行直到异步的完成没,其他依赖该模块的代码需要等待其加载完成。

// config.js(模块)
// 顶层await加载远程配置
export const config = await fetch('/api/config')
.then(res => res.json())
.catch(err => {
console.error('Failed to load config:', err);
return { default: 'config' };
});

// app.js(依赖config.js的模块)
import { config } from './config.js';
// 等待config加载完成后再执行
console.log('Loaded config:', config);
注意
  • 顶层 await 仅能在 ES 模块 (.mjs 或 package.json 设置 "type": "module")中使用,普通脚本(.js)不支持
  • 模块的执行顺序会因 await 暂停,需确保依赖关系的正确性

六、 at() 方法

访问数组/字符串最后一个元素,之前需要使用 arr[arr.length - 1] 实现,代码冗长。现在可使用 at() 直接使用正负索引访问元素。

const arr = [10, 20, 30];

console.log(arr.at(-1)); // 30
console.log(arr.at(1)); // 10

const str = 'hello';

console.log(str.at(-1)); // 'o'
console.log(str.at(2)); // 'l'

七、 正则表达式匹配索引

正则匹配结果仅返回匹配内容,无法直接获取匹配的字符串在原文中的起始和结束位置,需额外计算。通过正则表达式的 d 标志,使 exec()match() 等方法返回的结果包含 indices 属性,记录匹配内容及捕获组的起始和结果索引。

const text = 'JavaScript ES2022';
const regex = /ES(\d+)/d; // 带 `d` 标志的正则

const match = regex.exec(text);
// "ES2022" (完整匹配)
console.log(match[0]);
// "2022" (捕获组)
console.log(match[1]);

// [[11, 17], [13, 17], groups: undefined]
console.log(match.indices);

八、 Error Cause (错误原因链)

多层捕获错误时,原始错误信息容易丢失,难以追踪根源。Error 构造函数新增 cause 选项,允许抛出新错误时附加原始错误,形成错误链。

async function fetchData() {
try {
const response = await fetch('/invalid-url');
if (!response.success) throw new Error('Request failed');
} catch (error) {
throw new Error('Failed to fetch data', { cause: error });
}
}

// 捕获上层错误
try {
await fetchData();
} catch (error) {
console.log(error.message);
console.log(error.cause);
}

九、 Object.hasOwn() 方法

检查对象自身是否包含某个属性时,传统方式 Object.prototype.hasOwnProperty.call(obj, prop) 代码冗长易出错。现新增 Object.hasOwn() 静态方法,直接检查对象自身是否有某属性(忽略原型链)。

const obj = { name: 'Tom' };

// 上古方式
console.log(Object.prototype.hasOwnProperty.call(obj, 'name')); // true

// 新方式
console.log(Object.hasOwn(obj, 'name')); // true
console.log(Object.hasOwn(obj, 'toString')); // false

十、 私有字段的 in 操作符检测优化

可以使用 in 操作符中使用 #fieldName 来检测一个对象是否包含了特定的私有字段,而不会抛出错误